#include <Arduino.h>
#include <U8g2lib.h>

// #ifdef U8X8_HAVE_HW_SPI
// #include <SPI.h>
// #endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

// //定时器计数器
// #define AGT1_AGT 0x40084100
// //定时器控制寄存器 启动定时器0x00
// #define AGT1_AGTCR 0X40084108
// //定时器模式设置 定时器模式0x50
// #define AGT1_AGTMR1 0x40084109
// //定时器模式设置
// #define AGT1.AGTMR2 0x4008410A

// U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

typedef unsigned char uchar;
typedef unsigned long ulong;

//step motor pin define
const uint8_t  a1=8;
const uint8_t  a2=9;
const uint8_t  b1=10;
const uint8_t  b2=11;

//ds1302 pin define
//使能端
const uint8_t RST=6;
//数据端
const uint8_t DAT=5;
//时钟端
const uint8_t CLK=4;

//ds18b20 pin define,不能用带PWM的口
const uint8_t DQ=2;

const char steps_const[8][4] PROGMEM ={
  {1,1,1,0},
  {1,1,0,0},
  {1,1,0,1},
  {1,0,0,1},
  {1,0,1,1},
  {0,0,1,1},
  {0,1,1,1},
  {0,1,1,0}
};


static const uchar fang_ch[] PROGMEM ={0x10,0x00,0x20,0x00,0xFF,0x07,0x08,0x00,0x08,0x00,0xF8,0x03,
0x08,0x02,0x08,0x02,0x04,0x02,0x04,0x02,0x02,0x02,0xC1,0x01,};/*"方",0*/

static const uchar xiang_ch[] PROGMEM ={0x10,0x00,0x08,0x00,0xFF,0x07,0x01,0x04,0x01,0x04,0xF9,0x04,
0x89,0x04,0x89,0x04,0x89,0x04,0xF9,0x04,0x01,0x04,0x01,0x07};/*"向",1*/

static const uchar bu_ch[] PROGMEM ={0x20,0x00,0x24,0x00,0xE4,0x03,0x24,0x00,0x24,0x00,0xFF,0x07,
0x00,0x00,0x28,0x02,0x24,0x01,0xE2,0x00,0x30,0x00,0x0E,0x00};/*"步",0*/

static const uchar shu_ch[] PROGMEM ={0x88,0x00,0xAA,0x00,0x88,0x00,0xBF,0x07,0x4C,0x02,0x9A,0x02,
0xA9,0x02,0x84,0x02,0x9F,0x02,0x12,0x01,0x8C,0x02,0x53,0x04};/*"数",1*/

static const uchar jiao_ch[] PROGMEM ={0x04,0x00,0xFC,0x00,0x82,0x00,0x41,0x00,0xFE,0x03,0x22,0x02,
0xFE,0x03,0x22,0x02,0xFE,0x03,0x22,0x02,0x22,0x02,0x01,0x03,};/*"角",0*/

static const uchar du_ch[] PROGMEM ={0x40,0x00,0xFE,0x07,0x12,0x01,0xFE,0x07,0x12,0x01,0xF2,0x01,
0x02,0x00,0xFA,0x03,0x12,0x02,0x22,0x01,0xC2,0x00,0x39,0x07,};/*"度",1*/

static const uchar du_fuhao[] PROGMEM ={0x00,0x00,0x0C,0x00,0x12,0x00,0x12,0x00,0x0C,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};/*"°",0*/

static const uchar wen_ch[] PROGMEM ={0xF0,0x01,0x11,0x01,0xF2,0x01,0x10,0x01,0xF0,0x01,0x01,0x00,
0xFA,0x03,0xA8,0x02,0xAC,0x02,0xAA,0x02,0xA9,0x02,0xFC,0x07};/*"温",0*/

static const uchar num[][40] PROGMEM={
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0x73,0xFE,0xFB,0xFC,0xF9,0xFC,0xF9,0xFC,
  0xF9,0xFC,0xF9,0xFC,0xF9,0xFC,0xF9,0xFE,0x73,0xFE,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"0",0*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0x8F,0xFF,0x93,0xFF,0x9F,0xFF,0x9F,0xFF,
  0x9F,0xFF,0x9F,0xFF,0x9F,0xFF,0x9F,0xFF,0x9F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"1",1*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0x7B,0xFC,0xFF,0xFC,0xFF,0xFC,0x7F,0xFC,
  0x3F,0xFE,0x8F,0xFF,0xE7,0xFF,0xF3,0xFF,0xF9,0xFF,0x01,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"2",2*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0x3B,0xFE,0x7F,0xFE,0x7F,0xFE,0x3F,0xFF,
  0x83,0xFF,0x7F,0xFE,0xFF,0xFC,0xFF,0xFC,0x7D,0xFE,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"3",3*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFE,0x3F,0xFE,0x1F,0xFE,0x4F,0xFE,0x67,0xFE,
  0x73,0xFE,0x79,0xFE,0x00,0xFC,0x7F,0xFE,0x7F,0xFE,0x7F,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"4",4*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0xFE,0xF3,0xFF,0xF3,0xFF,0xF3,0xFF,0xF3,0xFF,
  0x03,0xFF,0x7F,0xFC,0xFF,0xFC,0xFF,0xFC,0x7F,0xFE,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"5",5*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFE,0xE7,0xFF,0xF3,0xFF,0xF9,0xFF,0x09,0xFF,
  0x71,0xFE,0xF9,0xFC,0xF9,0xFC,0xF9,0xFC,0x73,0xFE,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"6",6*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0xFC,0xFF,0xFE,0x7F,0xFE,0x3F,0xFF,0xBF,0xFF,
  0x9F,0xFF,0xCF,0xFF,0xCF,0xFF,0xE7,0xFF,0xE7,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"7",7*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0x63,0xFC,0xF3,0xFC,0xF3,0xFC,0x67,0xFE,
  0x07,0xFE,0x73,0xFC,0xF9,0xFC,0xF9,0xFC,0x73,0xFE,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"8",8*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0xFF,0x73,0xFE,0xF9,0xFC,0xF9,0xFC,0xF9,0xFC,
  0x73,0xFC,0x87,0xFC,0xFF,0xFC,0x7F,0xFE,0x3B,0xFF,0x87,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},/*"9",9*/
  {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF,0xF9,0xFF,
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}/*":"*/
};

void setup() {
  // put your setup code here, to run once:
  pinMode(a1,OUTPUT);
  pinMode(a2,OUTPUT);
  pinMode(b1,OUTPUT);
  pinMode(b2,OUTPUT);
  randomSeed(analogRead(A0));
  Serial.begin(9600);
  //init ds1302 pin
  pinMode(RST,OUTPUT);
  pinMode(CLK,OUTPUT);
  //初始化屏幕
  u8g2.begin();
  u8g2_prepare();
}

void u8g2_prepare() {
  u8g2.enableUTF8Print();
  u8g2.setContrast(10);
  // u8g2.setFont(u8g2_font_unifont_t_chinese3);
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setFontRefHeightExtendedText();
  u8g2.setDrawColor(1);
  u8g2.setFontPosTop();
  u8g2.setFontDirection(0);
}

int stepsOfARevolution=4096;
// //rpm of every minutessss
// uchar stepSpeed=2;
float tempVal=0.0;
float vol=0.0;
ulong next_refresh_time = 0;
ulong next_convert_time=0;
uint8_t randomDir=0;
int stepNum=0;
void loop() {
  convertTemp();
  startAnimation();
  while(true){
    randomDir = random(0, 2);
    stepNum = random(1, stepsOfARevolution);
    //get ds18b20 temperature
    ulong millisTime=millis();
    if(millisTime>=next_convert_time){
      tempVal = readTemp();
      Serial.print("get temperature:");
      Serial.println(tempVal);
      convertTemp();
      next_convert_time=millisTime+5000;
    }
    if(millisTime>=next_refresh_time){
      int volAdc=analogRead(A1);
      vol=1.65/volAdc*1024;
      Serial.print("Board Voltage:");
      Serial.println(vol);
      next_refresh_time=millisTime+1000;
    }
    showOled();
    stepMotor();
    delay(150);
  }
}

void showOled(){
  // 将整数转换为字符串  
  char stepStr[6]; // 假设步数最多为4096，加上终止符'\0'  
  snprintf(stepStr, sizeof(stepStr), "%d", stepNum);  
  u8g2.firstPage();
  do{
    viewTime();
    u8g2.drawXBMP(0, 0, 12, 12, fang_ch);
    u8g2.drawXBMP(14, 0, 12, 12, xiang_ch);
    u8g2.setCursor(28,1);
    u8g2.print(":");
    // u8g2.drawStr(0,0"方向:");
    if(randomDir==0){
      u8g2.drawStr(32,1,"CCW");
    }else{
      u8g2.drawStr(32,1,"CW");
    }
    //输出温度
    u8g2.drawXBMP(60,0,12,12,wen_ch);
    u8g2.drawXBMP(74,0,12,12,du_ch);
    u8g2.drawStr(88,1,":");
    char tempStr1[3];
    snprintf(tempStr1,sizeof(tempStr1),"%d",int(tempVal));
    u8g2.drawStr(92,2,tempStr1);
    u8g2.drawStr(tempVal>0?105:110,2,".");
    char tempStr2[2];
    snprintf(tempStr2,sizeof(tempStr2),"%d",int(tempVal*10)%10);
    u8g2.drawStr(tempVal>0?110:115,2,tempStr2);
    u8g2.drawXBMP(97+6*(tempVal>0?3:4),2,12,12,du_fuhao);
    //输出步数
    u8g2.drawXBMP(0, 14, 12, 12, bu_ch);
    u8g2.drawXBMP(14, 14, 12, 12, shu_ch);
    // u8g2.drawStr(0,36,"步数:");
    u8g2.setCursor(28,16);
    u8g2.print(":");
    u8g2.drawStr(32, 16, stepStr);
    //输出角度
    u8g2.drawXBMP(60,14,12,12,jiao_ch);
    u8g2.drawXBMP(74,14,12,12,du_ch);
    u8g2.drawStr(88,16,":");
    float angle=360.0*(float(stepNum)/float(stepsOfARevolution));
    char angleStr[6];
    snprintf(angleStr,sizeof(angleStr),"%d",int(angle));
    u8g2.drawStr(92,16,angleStr);
    u8g2.drawXBMP(92+6*getNumLength(int(angle)),16,12,12,du_fuhao);
    //输出电压
    int voltage=int(vol*100);
    char volChar1[3];
    snprintf(volChar1,sizeof(volChar1),"%d",voltage/100);
    u8g2.drawStr(90,28,volChar1);
    u8g2.drawStr(97,28,".");
    char volChar2[5];
    snprintf(volChar2,sizeof(volChar2),"%d",voltage%100);
    u8g2.drawStr(101,28,volChar2);
    u8g2.drawStr(114,28,"v");
  }while(u8g2.nextPage());
}

//starting animation
void startAnimation(){
  u8g2.drawStr(20,20 , "MCU Starting!");
  u8g2.drawStr(20,32 , "Oled Check...");
  u8g2.sendBuffer();
  delay(2000);
  for(char i=0;i<64;i++){
    u8g2.firstPage();
    do{
      u8g2.drawStr(20,20 , "MCU Starting!");
      u8g2.drawStr(20,32 , "Oled Check...");
      u8g2.drawBox(0, 0, 2*i, i);
    }while(u8g2.nextPage());
  }
  for(char i=0;i<64;i++){
    u8g2.firstPage();
    do{
      u8g2.drawBox(2*i, i, 128-4*i, 64-2*i);
    }while(u8g2.nextPage());
  }
}

//格式化数字，至少保留两位；比如5格式化后变成05
void formatNum(int params,char buffer[]){
  sprintf(buffer,"%02d",params);
}

//获取数字长度
int getNumLength(int params){
  int length=0;
  while(params!=0){
    params=params/10;
    length++;
  }
  return length;
}

void stepMotor(){
  char index=0;
  for(int i=0;i<stepNum;i++){
    if (randomDir) {  
      // 正向旋转
      digitalWrite(a1, steps_const[index][0]);
      digitalWrite(a2, steps_const[index][1]);
      digitalWrite(b1, steps_const[index][2]);
      digitalWrite(b2, steps_const[index][3]);
    } else {
      // 反向旋转  
      digitalWrite(a1, steps_const[7 - index][0]);
      digitalWrite(a2, steps_const[7 - index][1]);
      digitalWrite(b1, steps_const[7 - index][2]);
      digitalWrite(b2, steps_const[7 - index][3]);
    }
    if(index==7){
      index=0;
    }else{
      index++;
    }
    delay(1);
    //  ulong millisTime=millis();
    // if(millisTime>=next_refresh_time){
    //   showOled(dir,steps);
    //   next_refresh_time=millisTime+1000;
    // }
    // delayMicroseconds(850);
  }
  digitalWrite(a1, LOW);
  digitalWrite(a2, LOW);
  digitalWrite(b1, LOW);
  digitalWrite(b2, LOW);
}

/***************************************about ds1302 code**************************************/
void viewTime(){
  uchar years=bcd2DEC(readDs1302Data(0x8d));
  uchar months=bcd2DEC(readDs1302Data(0x89));
  uchar dates=bcd2DEC(readDs1302Data(0x87));
  uchar hours=bcd2DEC(readDs1302Data(0x85));
  uchar minutes=bcd2DEC(readDs1302Data(0x83));
  uchar seconds=bcd2DEC(readDs1302Data(0x81));
  // Serial.print("DS1302 Time：");
  // Serial.print(20);
  // Serial.print(years);
  // Serial.print("-");
  // Serial.print(months);
  // Serial.print("-");
  // Serial.print(dates);````
  // Serial.print(" ");
  // Serial.print(hours);
  // Serial.print(":");
  // Serial.print(minutes);
  // Serial.print(":");
  // Serial.print(seconds);
  // Serial.println();

  char monthsBuffer[5];
  formatNum(months,monthsBuffer);
  char datesBuffer[5];
  formatNum(dates,datesBuffer);
  char hoursBuffer[5];
  formatNum(hours,hoursBuffer);
  char minutesBuffer[5];
  formatNum(minutes,minutesBuffer);
  char secondsBuffer[5];
  formatNum(seconds,secondsBuffer);

  char timeStr[30];
  // snprintf(timeStr,sizeof(timeStr),"%s%d%d%s%s%s%s%s%s%s%s%s%s","Rtc:",20,years,"-",monthsBuffer,"-",datesBuffer," ",hoursBuffer,":",minutesBuffer,":",secondsBuffer);
  snprintf(timeStr,sizeof(timeStr),"%s%d%d%s%s%s%s","Rtc:",20,years,"-",monthsBuffer,"-",datesBuffer);
  u8g2.setCursor(0,28);
  u8g2.print(timeStr);

  uchar hours1=hours/10;
  uchar hours2=hours%10;
  u8g2.drawXBMP(1,45,12,20,num[hours1]);
  u8g2.drawXBMP(13,45,12,20,num[hours2]);
  u8g2.drawXBMP(25,45,12,20,num[10]);

  uchar minutes1=minutes/10;
  uchar minutes2=minutes%10;
  u8g2.drawXBMP(37,45,12,20,num[minutes1]);
  u8g2.drawXBMP(49,45,12,20,num[minutes2]);
  u8g2.drawXBMP(61,45,12,20,num[10]);

  uchar seconds1=seconds/10; 
  uchar seconds2=seconds%10;
  u8g2.drawXBMP(73,45,12,20,num[seconds1]);
  u8g2.drawXBMP(85,45,12,20,num[seconds2]);
  u8g2.drawBox(97, 45, 31, 20);
}

void writeDs1302Commond(char cmd){
  //make sure RST,CLK is Low before begin to send data;
  pinMode(DAT,OUTPUT);
  digitalWrite(RST,LOW);
  digitalWrite(CLK,LOW);
  digitalWrite(RST,HIGH);
  for(char i=0;i<8;i++){
    char sendBit = 0;
    sendBit = cmd & 0x01;
    digitalWrite(CLK,LOW);
    digitalWrite(DAT,sendBit);
    //上升沿写出数据
    digitalWrite(CLK,HIGH);
    cmd=cmd >> 1;
  }
}

uchar readDs1302Data(char cmd){
  //首先写入要读取ds1302的寄存器地址
  writeDs1302Commond(cmd);
  //读取数据
  pinMode(DAT,INPUT);
  uchar reciveByte=0;
  for(char i=0;i<8;i++){
    char readBit=0;
    digitalWrite(CLK,LOW);
    readBit=digitalRead(DAT);
    //下降沿读取数据
    digitalWrite(CLK,HIGH);
    if(readBit){
      reciveByte= reciveByte | 0x80;
    }
    if(i!=7){
      reciveByte >>= 1;
    }
  }
  digitalWrite(RST,LOW);
  return reciveByte;
}

void writeDs1302Data(char cmd,uchar data){
  //写入ds1302要写入寄存器的地址
  writeDs1302Commond(cmd);
  //十进制转成BCD码
  uchar new_data=dec2BCD(data);
  //写入数据
  for(char i=0;i<8;i++){
    digitalWrite(CLK,LOW);
    char sendBit=new_data & 0x01;
    digitalWrite(DAT,sendBit);
    //上升沿写出数据
    digitalWrite(CLK,HIGH);
    new_data>>=1;
  }
  digitalWrite(RST,LOW);
}

uchar bcd2DEC(uchar bcd){
  uchar num1 = bcd/16;
  uchar num2 = bcd%16;
  return (num1*10+num2);
}

uchar dec2BCD(uchar data){
  uchar num1=data/10;
  uchar num2=data%10;
  return (num1*16+num2);
}

void convertTemp(){
  bool isInited=initDs18b20();
  if(isInited){
    sendDs18b20Commd(0xcc);
    sendDs18b20Commd(0x44);
  }else{
    Serial.print("no ds18b20 reponse!");
  }
}

float readTemp(){
  bool isInited=initDs18b20();
  float res=0;
  int tempVal=0;
  bool isNegative;
  if(isInited){
    sendDs18b20Commd(0xcc);
    sendDs18b20Commd(0xbe);
    uint8_t LSB=readDs18b20();
    uint8_t HSB=readDs18b20();
    isNegative=HSB>>7;
    tempVal = LSB|(HSB&0x07)<<8;
  }else{
    Serial.print("no ds18b20 reponse!");
    return 0.0;
  }
  res=tempVal*0.0625;
  if(isNegative){
    return -res;
  }else{
    return res;
  }
}

bool initDs18b20(){
  pinMode(DQ,OUTPUT);
  digitalWrite(DQ,LOW);
  delayMicroseconds(500);
  pinMode(DQ,INPUT);
  delayMicroseconds(16);
  int index=0;
  bool res=0;
  while(index<=480){
    if(!digitalRead(DQ)){
      res=1;
    }
    delayMicroseconds(1);
    index++;
  }
  return res;
}

void sendDs18b20Commd(char comm){
  pinMode(DQ,OUTPUT);
  for(int i=0;i<8;i++){
    int bit=comm&0x01;
    digitalWrite(DQ,LOW);
    delayMicroseconds(15);
    if(bit==1){
      digitalWrite(DQ,HIGH);
    }
    delayMicroseconds(45);
    digitalWrite(DQ,HIGH);
    delayMicroseconds(2);
    comm=comm>>1;
  }
}

uint8_t readDs18b20(){
  uint8_t returnVal;
  pinMode(DQ,OUTPUT);
  for(uint8_t i=0;i<8;i++){
    pinMode(DQ,OUTPUT);
    digitalWrite(DQ,LOW);
    delayMicroseconds(5);
    pinMode(DQ,INPUT);
    delayMicroseconds(15);
    uint8_t index=0;
    uint8_t bit=1;
    while(index<50){
      if(!digitalRead(DQ)){
        bit=0;
      }
      delayMicroseconds(1);  
      index++;
    }
    if(bit==1){
      returnVal=returnVal|0x80;
    }
    if(i!=7){
      returnVal=returnVal>>1;
    }
  }
  return returnVal;
}